﻿// -----------------------------------------------------------------------
// <copyright file="ConfigSettingsBase.cs" company="OpenSource">
//     All code is Microsoft Public License (Ms-PL)
// </copyright>
// <history>
// 2013.02      Created     Chris Hand
// </history>
// -----------------------------------------------------------------------
namespace OpenSource.OpenDMS.Common.Configuration
{
    using System;
    using System.Linq;
    using System.Xml.Linq;
    using OpenSource.OpenDMS.Common.Logging;

    /// <summary>
    /// Athena Configuration Information
    /// </summary>
    public abstract class ConfigSettingsBase
    {
        /// <summary>
        /// The name of the custom section that will be looked at
        /// </summary>
        private string settingsName;

        /// <summary>
        /// Initializes a new instance of the ConfigSettingsBase class.
        /// </summary>
        /// <param name="settingsName">The name of the custom section that will be looked at</param>
        public ConfigSettingsBase(string settingsName)
        {
            this.settingsName = settingsName;
        }

        /// <summary>
        /// Gets or sets the logger
        /// </summary>
        public ILogger Logger
        {
            get;
            protected set;
        }

        /// <summary>
        /// configure the setting on Load
        /// </summary>
        public void Load()
        {
            XDocument config = this.ReadSettings();
            this.ParseSettings(config);
        }

        /// <summary>
        /// Figure out which configuration we want to load
        /// </summary>
        /// <param name="machines">The list of machines listed in the app.config file</param>
        /// <param name="machineName">The current machine name</param>
        /// <param name="defaultSetting">The name of the config to use if the machine name is not found in machines</param>
        /// <returns>The name of the config to use</returns>
        protected virtual string DetermineSettingName(XElement machines, string machineName, string defaultSetting)
        {
            XElement machine = (from foo in machines.Descendants("Machine")
                                where foo.Attribute("name").Value.ToLower().Equals(machineName)
                                select foo).FirstOrDefault();
            if (null == machine)
            {
                return defaultSetting;
            }

            return machine.Attribute("setting").Value;
        }

        /// <summary>
        /// Load the machines section 
        /// </summary>
        /// <param name="config">The full app.config file</param>
        /// <returns>The Machines section of the app.config file</returns>
        protected virtual XElement GetMachinesSection(XDocument config)
        {
            XElement result = config.Element("configuration").Element(this.settingsName).Element("Machines");
            return result;
        }

        /// <summary>
        /// From within the machines section, find the machine name and get the specified setting name
        /// </summary>
        /// <param name="config">The full app.config file</param>
        /// <param name="machineName">The machine name find the settings for</param>
        /// <returns>The name of the setting to load.  If the machine name wasn't found, return null</returns>
        protected virtual string GetMachineSettingSelection(XDocument config, string machineName)
        {
            XElement settings = (from foo in config.Descendants("Machines").Descendants("Machine")
                                 where foo.Attribute("name").Value.ToLower().Equals(machineName)
                                 select foo).FirstOrDefault();

            if (null == settings)
            {
                return null;
            }
            else
            {
                return settings.Attribute("setting").Value;
            }
        }

        /// <summary>
        /// Gets the settings section from the app.config file
        /// </summary>
        /// <param name="config">The app.config file</param>
        /// <returns>The settings section</returns>
        protected virtual XElement GetSettingsSection(XDocument config)
        {
            XElement result = config.Element("configuration").Element(this.settingsName).Element("Settings");
            return result;
        }

        /// <summary>
        /// Read settings
        /// </summary>
        /// <returns>XDocument object</returns>
        protected virtual XDocument ReadSettings()
        {
            return XDocument.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
        }

        /// <summary>
        /// Parse settings from xml
        /// </summary>
        /// <param name="config">config file as XDocument</param>
        protected virtual void ParseSettings(XDocument config)
        {
            XElement settings = this.GetSettingsSection(config);
            XElement machines = this.GetMachinesSection(config);
            string machineName = Environment.MachineName.ToLower();
            string defaultSetting = settings.Attribute("default").Value;
            string settingName = this.DetermineSettingName(machines, machineName, defaultSetting);
            XElement machineSettings = this.SelectMachineSettings(settings, settingName);
            this.ParseSettings(machineSettings);
        }

        /// <summary>
        /// From the provided machine settings, parse out the attributes and set this objects properties
        /// </summary>
        /// <param name="machineSettings">The machine settings to parse</param>
        protected abstract void ParseSettings(XElement machineSettings);

        /// <summary>
        /// From the collection of settings, find the settings for the provided name
        /// </summary>
        /// <param name="settings">The collection of settings</param>
        /// <param name="settingName">The name of the settings to find</param>
        /// <returns>The specific settings</returns>
        protected virtual XElement SelectMachineSettings(XElement settings, string settingName)
        {
            XElement machineSettings = (from foo in settings.Descendants("Setting")
                                        where foo.Attribute("name").Value.ToLower().Equals(settingName)
                                        select foo).FirstOrDefault();
            return machineSettings;
        }

        /// <summary>
        /// Convert the string of flag settings in the app.config file to LogFlag values
        /// Is there a smarter way?
        /// </summary>
        /// <param name="flagString">The flag values as a pipe delimited string</param>
        /// <returns>The LogFlag value</returns>
        protected virtual LogFlag ParseLogFlags(string flagString)
        {
            LogFlag result = LogFlag.Fatal;
            LogFlag parsed = LogFlag.Fatal;

            string[] flags = flagString.Split('|');
            foreach (string flag in flags)
            {
                if (Enum.TryParse<LogFlag>(flag.Trim(), out parsed))
                {
                    result |= parsed;
                    parsed = LogFlag.Fatal;
                }
            }

            return result;
        }

        /// <summary>
        /// Attempts to get the attribute value, throws a helpful exception if the attribute can't be found
        /// </summary>
        /// <param name="element">The machine element to pull from</param>
        /// <param name="attribute">The attribute to get</param>
        /// <returns>The value of the attribute</returns>
        protected string ReadAttribute(XElement element, string attribute)
        {
            XAttribute attrib = element.Attribute(attribute);
            if (null == attrib)
            {
                throw new ConfigException(string.Format("Could not find the setting attibute '{0}' - If it exists, double check the case", attribute));
            }

            return attrib.Value;
        }
    }
}

